#include "SceneGrpah.h"
#include "RenderTarget.h"
#include "RenderQueue.h"
#include "TriMesh.h"
#include "BasicGeometry.h"

namespace Nezha
{

	class BuiltInMeshCreator : public SceneObjectFactory
	{
	public:
		class Param : public SceneObjectCreation
		{
		public:
			BuiltInMesh mMeshType;
			u16 mXExtent;
			u16 mYExtent;

			Param()
				:SceneObjectCreation(SOT_GEOMETRY),
				mMeshType(BIM_PLANE),
				mXExtent(1),
				mYExtent(1)
			{
			}
		};

		BuiltInMeshCreator()
		{
			mSceneObjectType = SOT_GEOMETRY;
		}

	protected:
		SceneObject* createImpl(const _string& name, SceneObjectCreation* params)
		{
			Param* p = static_cast<Param*>(params);

			TriangleListMesh* tlm = NULL;

			switch(p->mMeshType)
			{
			case BIM_BOX:
				{
					tlm = TriangleListMesh::CreateBox(1.0f, 1.0f, 1.0f);
				}
				break;
			case BIM_PLANE:
				{
					tlm = TriangleListMesh::CreatePlane(p->mXExtent, p->mYExtent);
				}
				break;
			case BIM_SPHERE:
				{
					tlm = TriangleListMesh::CreateSphere(p->mXExtent, p->mYExtent, 1.0f);
				}
				break;
			}

			if(tlm)
			{
				BasicGeometry* bg = NZ_New BasicGeometry(name, static_cast<RenderMesh*>(tlm));
				return static_cast<SceneObject*>(bg);
			}

			return NULL;
		}
	};

	SceneGraph::SceneGraph()
	{
		mBulitInMeshCreator = NZ_New BuiltInMeshCreator();
	}

	SceneGraph::~SceneGraph()
	{
		if(mBulitInMeshCreator)
		{
			NZ_Delete mBulitInMeshCreator;
		}
	}

	Camera* SceneGraph::createCamera(const _string& name, bool perspective)
	{
		if(findObject(name))
		{
			NZError("Object %s already exsit, failed to create camera.", name.c_str());

			return NULL;
		}

		Camera* cam = NZ_New Camera(name, perspective);
		_insertObject(static_cast<SceneObject*>(cam));

		return cam;
	}

	Camera* SceneGraph::getCamera(const _string& name)
	{
		SceneObject* so = findObject(name);
		if(so->getSceneObjectType() == SOT_CAMERA)
		{
			return static_cast<Camera*>(so);
		}

		return NULL;
	}

	void SceneGraph::destroyCamera(Camera* cam)
	{
		if(cam)
		{
			destroySceneObject(static_cast<SceneObject*>(cam));
		}
	}

	SceneObject* SceneGraph::createFromScript(const _string& name, const _string& scriptName, SceneNode* bindNode /* = NULL */)
	{
		if(findObject(name))
		{
			NZError("Object %s already exsit, when try to create it from script.", name.c_str());

			return NULL;
		}

		SceneObjectCreation* params = loadScript(scriptName);
		if(params)
		{
			return create(name, params, bindNode);
		}

		return NULL;
	}

	SceneObject* SceneGraph::createBuiltInBox(const _string& name)
	{
		if(findObject(name))
		{
			NZError("Dumplicate object named %s", name.c_str());
			return NULL;
		}

		BuiltInMeshCreator::Param p;
		p.mMeshType = BIM_BOX;

		SceneObject* so = mBulitInMeshCreator->create(name, static_cast<SceneObjectCreation*>(&p));
		if(so)
		{
			_insertObject(so);
		}

		return so;
	}

	SceneObject* SceneGraph::createBuiltInPlane(const _string& name, u16 xExtent, u16 yExtent)
	{
		if(findObject(name))
		{
			NZError("Dumplicate object named %s", name.c_str());
			return NULL;
		}

		BuiltInMeshCreator::Param p;
		p.mMeshType = BIM_PLANE;
		p.mXExtent = xExtent;
		p.mYExtent = yExtent;

		SceneObject* so = mBulitInMeshCreator->create(name, static_cast<SceneObjectCreation*>(&p));
		if(so)
		{
			_insertObject(so);
		}

		return so;
	}

	SceneObject* SceneGraph::createBuiltInSphere(const _string& name)
	{
		if(findObject(name))
		{
			NZError("Dumplicate object named %s", name.c_str());
			return NULL;
		}

		BuiltInMeshCreator::Param p;
		p.mMeshType = BIM_SPHERE;
		p.mXExtent = 5;
		p.mYExtent = 10;

		SceneObject* so = mBulitInMeshCreator->create(name, static_cast<SceneObjectCreation*>(&p));
		if(so)
		{
			_insertObject(so);
		}

		return so;
	}

	void SceneGraph::cull(RenderTarget* rt, RenderQueue* opaRq, RenderQueue* transRq)
	{
		Camera* cam = rt->getCamera();

		SceneNode* sn = mRootNode.get();

		_cullByCamera(cam, sn, opaRq, transRq);
	}

	void SceneGraph::_cullByCamera(Camera* cam, SceneNode* sn, RenderQueue* opaRq, RenderQueue* transRq)
	{
		u32 numChildren = sn->getNumChildren();
		for(u32 i = 0;i < numChildren; i++)
		{
			SceneNode* sub = sn->getChild(i);
			
			// TODO cull by camera

			SceneObject* so = sub->getBindedObject();
			if(!so)
			{
				// not culled by camera
				RenderObject* ro = so->getRenderObject();
				if(ro)
				{
					// FIXME tes if it was batched.
					// FIXME test if it has transparent pass.
					opaRq->addRenderObject(ro);
				}
			}

			_cullByCamera(cam, sub, opaRq, transRq);
		}
	}

}//end namespace Nezha